﻿"""
主控制器
协调各个模块的工作
"""

import datetime
import time
import cv2
from typing import Dict, List, Tuple, Optional
from src.models.database import DatabaseManager
from src.models.face_recognition import FaceRecognitionModel
from src.utils.helpers import CameraManager, AttendanceManager, FontManager, FileManager
from src.config.settings import FACE_RECOGNITION_CONFIG, CAMERA_CONFIG


class AttendanceController:
    """考勤系统主控制器"""
    
    def __init__(self):
        # 初始化各个管理器
        self.db_manager = DatabaseManager()
        self.face_model = FaceRecognitionModel()
        self.camera_manager = CameraManager()
        self.attendance_manager = AttendanceManager()
        self.font_manager = FontManager()
        self.file_manager = FileManager()
        
        # 识别控制参数
        self.recognition_interval = FACE_RECOGNITION_CONFIG['recognition_interval']
        self.mirror_mode = FACE_RECOGNITION_CONFIG['mirror_mode']
        self.last_recognition_time = 0
        self.last_recognition_result = None
        
        # 加载数据
        self.load_all_data()
    
    def load_all_data(self):
        """加载所有数据"""
        # 加载员工数据
        employees_data = self.db_manager.get_all_employees()
        self.face_model.load_known_faces(employees_data)
        
        # 加载今日考勤数据
        self.load_today_attendance()
    
    def load_today_attendance(self):
        """加载今日考勤记录"""
        today = datetime.datetime.now().strftime("%Y-%m-%d")
        attendance_records = self.db_manager.get_attendance_by_date(today)
        
        self.attendance_manager.today_attendance = {}
        for employee_id, name, check_in_time, status in attendance_records:
            self.attendance_manager.add_attendance_record(employee_id, name, check_in_time, status)
    
    def add_employee(self, name: str, employee_id: str, face_image) -> bool:
        """添加员工"""
        # 检查员工是否已存在
        if self.db_manager.check_employee_exists(employee_id):
            return False
        
        # 提取人脸特征
        features = self.face_model.extract_face_features(face_image)
        if features is None:
            return False
        
        # 保存到数据库
        success = self.db_manager.add_employee(name, employee_id, face_image, features)
        if success:
            # 添加到人脸模型
            self.face_model.add_known_face(employee_id, name, face_image, features)
        
        return success
    
    def record_attendance(self, employee_id: str, name: str) -> str | None:
        """记录考勤"""
        try:
            current_time = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
            current_date = datetime.datetime.now().strftime("%Y-%m-%d")
            
            # 检查今天是否已经打卡
            if self.attendance_manager.is_attended_today(employee_id):
                # 检查是否可以显示"已打卡"消息（5秒间隔）
                if self.attendance_manager.can_display_message(employee_id, "already_checked"):
                    attendance_record = self.attendance_manager.get_attendance_record(employee_id)
                    check_in_time = attendance_record['check_in_time']
                    # 更新显示时间，防止频繁提示
                    self.attendance_manager.update_display_time(employee_id, "already_checked")
                    return f"{name} 今日已打卡 ({check_in_time})"
                return None  # 不显示消息，避免频繁提示
            
            # 检查打卡间隔
            if not self.attendance_manager.can_check_in(employee_id):
                # 检查是否可以显示"打卡过于频繁"消息（5秒间隔）
                if self.attendance_manager.can_display_message(employee_id, "too_frequent"):
                    self.attendance_manager.update_display_time(employee_id, "too_frequent")
                    return f"{name} 打卡过于频繁，请等待{self.attendance_manager.check_in_interval}秒"
                return None  # 不显示消息，避免频繁提示
            
            # 记录考勤到数据库
            success = self.db_manager.add_attendance(employee_id, current_time, current_date, "正常")
            if not success:
                return f" 打卡记录失败"
            
            # 更新本地记录
            self.attendance_manager.add_attendance_record(employee_id, name, current_time, "正常")
            self.attendance_manager.update_check_in_time(employee_id)
            self.attendance_manager.update_display_time(employee_id, "success")
            
            return f" {name} 打卡成功 - {current_time}"
            
        except Exception as e:
            return f" 打卡记录失败: {str(e)}"
    
    def process_frame(self, frame) -> Tuple[any, str]:
        """处理摄像头帧"""
        try:
            if not self.camera_manager.is_opened():
                return frame, ""
            
            # 检查帧是否有效
            if frame is None or frame.size == 0:
                return frame, ""
            
            # 应用镜像效果
            if self.mirror_mode:
                frame = self.apply_mirror_effect(frame)
            
            # 检测人脸
            faces = self.face_model.detect_faces(frame)
            current_time = time.time()
            
            # 添加识别间隔控制
            should_recognize = (current_time - self.last_recognition_time >= self.recognition_interval)
            
            # 保存当前帧的人脸位置
            current_face_rects = []
            message = ""
            
            if len(faces) > 0:
                for (x, y, w, h) in faces:
                    # 绘制人脸框
                    cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 255, 0), 2)
                    current_face_rects.append((x, y, w, h))
                    
                    if should_recognize:
                        # 提取人脸区域进行识别
                        face_roi = frame[y:y + h, x:x + w]
                        name, similarity = self.face_model.recognize_face(face_roi)
                        
                        if name:
                            # 保存识别结果和人脸位置
                            self.last_recognition_result = {
                                'name': name,
                                'similarity': similarity,
                                'face_rect': (x, y, w, h),
                                'timestamp': current_time
                            }
                            
                            # 显示中文姓名和相似度
                            display_text = f"{name} ({similarity:.2f})"
                            frame = self.font_manager.put_chinese_text(frame, display_text, (x, y - 30), 20, (0, 255, 0))
                            
                            # 自动打卡
                            employee_id = self.face_model.get_employee_id_by_name(name)
                            if employee_id:
                                result = self.record_attendance(employee_id, name)
                                if result:
                                    message = result
                        else:
                            # 识别失败，显示中文提示
                            self.last_recognition_result = {
                                'name': None,
                                'similarity': 0,
                                'face_rect': (x, y, w, h),
                                'timestamp': current_time
                            }
                            frame = self.font_manager.put_chinese_text(frame, "未知人员", (x, y - 30), 20, (0, 0, 255))
            
            # 在识别间隔期间显示上一次的识别结果
            if not should_recognize and self.last_recognition_result:
                result = self.last_recognition_result
                last_x, last_y, last_w, last_h = result['face_rect']
                
                # 寻找与上一次识别位置最接近的当前人脸框
                closest_face = None
                min_distance = float('inf')
                
                for (curr_x, curr_y, curr_w, curr_h) in current_face_rects:
                    # 计算两个矩形中心点的距离
                    last_center_x = last_x + last_w / 2
                    last_center_y = last_y + last_h / 2
                    curr_center_x = curr_x + curr_w / 2
                    curr_center_y = curr_y + curr_h / 2
                    
                    distance = ((last_center_x - curr_center_x) ** 2 +
                               (last_center_y - curr_center_y) ** 2) ** 0.5
                    
                    # 如果距离更小且在人脸移动的合理范围内（100像素）
                    if distance < min_distance and distance < 100:
                        min_distance = distance
                        closest_face = (curr_x, curr_y, curr_w, curr_h)
                
                # 如果找到匹配的人脸框，显示上一次的识别结果
                if closest_face:
                    curr_x, curr_y, curr_w, curr_h = closest_face
                    
                    # 更新上一次识别结果的位置为当前人脸框位置
                    self.last_recognition_result['face_rect'] = closest_face
                    
                    if result['name']:
                        # 显示上一次的识别结果
                        display_text = f"{result['name']} ({result['similarity']:.2f})"
                        frame = self.font_manager.put_chinese_text(frame, display_text, (curr_x, curr_y - 30), 20, (0, 255, 0))
                    else:
                        # 显示未知人员
                        frame = self.font_manager.put_chinese_text(frame, "未知人员", (curr_x, curr_y - 30), 20, (0, 0, 255))
            
            # 更新上次识别时间
            if should_recognize:
                self.last_recognition_time = current_time
            
            return frame, message
            
        except Exception as e:
            print(f"处理帧时出错: {e}")
            return frame, ""
    
    def apply_mirror_effect(self, frame):
        """应用镜像效果"""
        if self.mirror_mode:
            return cv2.flip(frame, 1)
        return frame
    
    def get_attendance_stats(self) -> dict:
        """获取考勤统计"""
        total_employees = len(self.face_model.get_known_faces())
        return self.attendance_manager.get_attendance_stats(total_employees)
    
    def get_today_attendance(self) -> dict:
        """获取今日考勤记录"""
        return self.attendance_manager.today_attendance
    
    def get_all_employees(self) -> List[Tuple]:
        """获取所有员工信息"""
        return self.db_manager.get_all_employees()
    
    def export_attendance_data(self, start_date: str, end_date: str, file_path: str) -> bool:
        """导出考勤数据"""
        data = self.db_manager.get_attendance_by_date_range(start_date, end_date)
        headers = ['姓名', '工号', '打卡时间', '日期', '状态']
        return self.file_manager.export_to_csv(data, headers, file_path)
    
    def close(self):
        """关闭系统"""
        self.camera_manager.close_camera()
        self.db_manager.close()
